home *** CD-ROM | disk | FTP | other *** search
- .xlist
- include stdlib.a
- includelib stdlib.lib
- .list
-
- cseg segment para public 'code'
- assume ds:nothing
-
- ; ScanCode must be in the Code segment.
-
- ScanCode db 0
-
-
-
- ;****************************************************************************
- ;
- ; KbdSim- Passes the scan code in AL through the keyboard controller
- ; using the trace flag. The way this works is to turn on the
- ; trace bit in the flags register. Each instruction then causes a trace
- ; trap. The (installed) trace handler then looks at each instruction to
- ; handle IN, OUT, INT, and other special instructions. Upon encountering
- ; an IN AL, 60 (or equivalent) this code simulates the instruction and
- ; returns the specified scan code rather than actually executing the IN
- ; instruction. Other instructions need special treatment as well. See
- ; the code for details. This code is pretty good at simulating the hardware,
- ; but it runs fairly slow and has a few compatibility problems.
-
-
- KbdSim proc near
-
- pushf
- push es
- push ax
- push bx
-
-
- xor bx, bx ;Point es at int vector tbl
- mov es, bx ; (to simulate INT 9).
- cli ;No interrupts for now.
- mov cs:ScanCode, al ;Save output scan code.
-
- push es:[1*4] ;Save current INT 1 vector
- push es:2[1*4] ; so we can restore it later.
-
-
-
- ; Point the INT 1 vector at our INT 1 handler:
-
- mov word ptr es:[1*4], offset MyInt1
- mov word ptr es:[1*4 + 2], cs
-
-
-
- ; Turn on the trace trap:
-
- pushf
- pop ax
- or ah, 1
- push ax
- popf
-
-
- ; Simulate an INT 9 instruction. Note: cannot actually execute INT 9 here
- ; since INT instructions turn off the trace operation.
-
-
- pushf
- call dword ptr es:[9*4]
-
-
- ; Turn off the trace operation:
-
-
- pushf
- pop ax
- and ah, 0feh ;Clear trace bit.
- push ax
- popf
-
-
- ; Disable trace operation.
-
-
- pop es:[1*4 + 2] ;Restore previous INT 1
- pop es:[1*4] ; handler.
-
-
- ; Okay, we're done. Restore registers and return.
-
- VMDone: pop bx
- pop ax
- pop es
- popf
- ret
- KbdSim endp
-
-
-
- ;----------------------------------------------------------------------------
- ;
- ; MyInt1- Handles the trace trap (INT 1). This code looks at the next
- ; opcode to determine if it is one of the special opcodes we have to
- ; handle ourselves.
-
-
- MyInt1 proc far
- push bp
- mov bp, sp ;Gain access to return adrs via BP.
- push bx
- push ds
-
- ; If we get down here, it's because this trace trap is directly due to
- ; our having punched the trace bit. Let's process the trace trap to
- ; simulate the 80x86 instruction set.
- ;
- ; Get the return address into DS:BX
-
- NextInstr: lds bx, 2[bp]
-
- ; The following is a special case to quickly eliminate most opcodes and
- ; speed up this code by a tiny amount.
-
- cmp byte ptr [bx], 0cdh ;Most opcodes are less than
- jnb NotSimpleInstr ; 0cdh, hence we quickly
- pop ds ; return back to the real
- pop bx ; program.
- pop bp
- iret
-
- NotSimpleInstr: je IsIntInstr ;If it's an INT instruction.
-
- mov bx, [bx] ;Get current instruction's opcode.
- cmp bl, 0e8h ;CALL opcode
- je ExecInstr
- jb TryInOut0
-
- cmp bl, 0ech ;IN al, dx instr.
- je MayBeIn60
- cmp bl, 0eeh ;OUT dx, al instr.
- je MayBeOut20
- pop ds ;A normal instruction if we get
- pop bx ; down here.
- pop bp
- iret
-
- TryInOut0: cmp bx, 60e4h ;IN al, 60h instr.
- je IsINAL60
- cmp bx, 20e6h ;out 20, al instr.
- je IsOut20
-
- ; If it wasn't one of our magic instructions, execute it and continue.
-
- ExecInstr: pop ds
- pop bx
- pop bp
- iret
-
- ; If this instruction is IN AL, DX we have to look at the value in DX to
- ; determine if it's really an IN AL, 60h instruction.
-
- MayBeIn60: cmp dx, 60h
- jne ExecInstr
- inc word ptr 2[bp] ;Skip over this 1 byte instr.
- mov al, cs:ScanCode
- jmp NextInstr
-
- ; If this is an IN AL, 60h instruction, simulate it by loading the current
- ; scan code into AL.
-
- IsInAL60: mov al, cs:ScanCode
- add word ptr 2[bp], 2 ;Skip over this 2-byte instr.
- jmp NextInstr
-
-
- ; If this instruction is OUT DX, AL we have to look at DX to see if we're
- ; outputting to location 20h (8259).
-
- MayBeOut20: cmp dx, 20h
- jne ExecInstr
- inc word ptr 2[bp] ;Skip this 1 byte instruction.
- jmp NextInstr
-
- ; If this is an OUT 20h, al instruction, simply skip over it.
-
- IsOut20: add word ptr 2[bp], 2 ;Skip instruction.
- jmp NextInstr
-
-
- ; IsIntInstr- Execute this code if it's an INT instruction.
- ;
- ; The problem with the INT instructions is that they reset the trace bit
- ; upon execution. For certain guys (see above) we can't have that.
- ;
- ; Note: at this point the stack looks like the following:
- ;
- ; flags
- ;
- ; rtn cs -+
- ; |
- ; rtn ip +-- Points at next instr the CPU will execute.
- ; bp
- ; bx
- ; ds
- ;
- ; We need to simulate the appropriate INT instruction by:
- ;
- ; (1) adding two to the return address on the stack (so it returns
- ; beyond the INT instruction.
- ; (2) pushing the flags onto the stack.
- ; (3) pushing a phony return address onto the stack which simulates
- ; the INT 1 interrupt return address but which "returns" us to
- ; the specified interrupt vector handler.
- ;
- ; All this results in a stack which looks like the following:
- ;
- ; flags
- ;
- ; rtn cs -+
- ; |
- ; rtn ip +-- Points at next instr beyond the INT instruction.
- ;
- ; flags -- Bogus flags to simulate those pushed by INT instr.
- ;
- ; rtn cs -+
- ; |
- ; rtn ip +-- "Return address" which points at the ISR for this INT instr.
- ; bp
- ; bx
- ; ds
-
-
- IsINTInstr: add word ptr 2[bp], 2 ;Bump rtn adrs beyond INT instr.
- mov bl, 1[bx]
- mov bh, 0
- shl bx, 1 ;Multiply by 4 to get vector
- shl bx, 1 ; address.
-
- push [bp-0] ;Get and save BP
- push [bp-2] ;Get and save BX.
- push [bp-4] ;Get and save DS.
-
- push cx
- xor cx, cx ;Point DS at interrupt
- mov ds, cx ; vector table.
-
- mov cx, [bp+6] ;Get original flags.
- mov [bp-0], cx ;Save as pushed flags.
-
- mov cx, ds:2[bx] ;Get vector and use it as
- mov [bp-2], cx ; the return address.
- mov cx, ds:[bx]
- mov [bp-4], cx
-
- pop cx
- pop ds
- pop bx
- pop bp
- iret
- ;
- MyInt1 endp
-
-
-
-
- ; Main program - Simulates some keystrokes to demo the above code.
-
- Main proc
-
- mov ax, cseg
- mov ds, ax
-
- print
- byte "Simulating keystrokes via Trace Flag",cr,lf
- byte "This program places 'DIR' in the keyboard buffer"
- byte cr,lf,0
-
- mov al, 20h ;"D" down scan code
- call KbdSim
- mov al, 0a0h ;"D" up scan code
- call KbdSim
-
- mov al, 17h ;"I" down scan code
- call KbdSim
- mov al, 97h ;"I" up scan code
- call KbdSim
-
- mov al, 13h ;"R" down scan code
- call KbdSim
- mov al, 93h ;"R" up scan code
- call KbdSim
-
- mov al, 1Ch ;Enter down scan code
- call KbdSim
- mov al, 9Ch ;Enter up scan code
- call KbdSim
-
-
-
- ExitPgm
- Main endp
-
-
- cseg ends
-
- sseg segment para stack 'stack'
- stk db 1024 dup ("stack ")
- sseg ends
-
- zzzzzzseg segment para public 'zzzzzz'
- LastBytes db 16 dup (?)
- zzzzzzseg ends
- end Main
-
-